﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;
using Microsoft.Xna.Framework;
using haptic.src.engine.collisions;
using haptic.src.engine.animation;
using System.Diagnostics;
using haptic.src.engine.core;
using haptic.src.engine.render;
using haptic.src.engine.maths;
using haptic.src.engine.debug;

namespace haptic.src.engine
{
    [Serializable]
    [XmlRootAttribute("node")]
    public class Node : IXmlSerializable
    {
        #region Enum
        public enum NodeType
        {
            E_DEFAULT = 0,
            E_ROOT,
            E_COLLISION,
            E_PHYSICS,
            E_SPRITE
        }
        [Flags]
        public enum Flag
        {
            E_NONE          = 0,
            E_LOCAL_CHANGED = (1<<0)
        }
        #endregion

        #region Constructors
        public Node()
        {
            m_eType = NodeType.E_DEFAULT;
        }
        #endregion

        #region Edit
        public void Link(Node oNode)
        {
            if (m_oChildren == null)
                m_oChildren = new List<Node>();
            m_oChildren.Add(oNode);
            oNode.m_oParent = this;
            oNode.m_oRoot = m_oRoot;
            EnableFlag(Flag.E_LOCAL_CHANGED);
        }
        public void Unlink(Node oNode)
        {
            Debug.Assert(m_oChildren != null);
            m_oChildren.Remove(oNode);
            oNode.m_oParent = null;
            oNode.m_oRoot = oNode;
            if (m_oChildren.Count == 0)
                m_oChildren = null;
        }
        public void EnableFlag(Flag eFlag)
        {
            m_eFlags |= eFlag;
        }
        public void DisableFlag(Flag eFlag)
        {
            m_eFlags &= ~eFlag;
        }
        public void SetFlag(Flag eFlag, bool bVal)
        {
            if (bVal)
                m_eFlags |= eFlag;
            else
                m_eFlags &= ~eFlag;
        }
        public bool GetFlag(Flag eFlag)
        {
            return (m_eFlags & eFlag) != 0;
        }
        public virtual void SetRootPos(Vector2 vPos)
        {
            m_mLocal.O = vPos;
            m_eFlags |= Flag.E_LOCAL_CHANGED;
        }
        #endregion

        #region Load
        public virtual Result Load()
        {
            bool bFailed = false;
            if (m_oChildren != null)
                foreach (Node oNode in m_oChildren)
                {
                    if (oNode.Load().IsFailed())
                        bFailed = true;
                }
            if (bFailed)
                return new Result();
            else
                return Result.Success;
        }
        public virtual void Unload()
        {

        }
        #endregion

        #region Update
        public virtual void Update(GameTime oTime)
        {
            if ((m_eFlags & Flag.E_LOCAL_CHANGED) != 0)
            {
                if (m_oChildren != null)
                {
                    foreach (Node oNode in m_oChildren)
                        oNode.EnableFlag(Flag.E_LOCAL_CHANGED);
                }
                if (m_oParent == null)
                    m_mWorld = m_mLocal;
                else
                    m_mWorld = m_oParent.m_mWorld * m_mLocal;
                m_eFlags &= ~Flag.E_LOCAL_CHANGED;
            }
        }
        #endregion

        #region Draw
        public virtual void Draw(Renderer oRenderer)
        {
        }
        public virtual void DrawDebug(DebugRenderer oRenderer)
        {
        }
        #endregion

        #region Serialization
        public XmlSchema GetSchema()
        {
            return null;
        }
        public void WriteXml(XmlWriter w)
        {
            XmlSerializerNamespaces oNs = new XmlSerializerNamespaces();
            oNs.Add("", "");

            // type
            w.WriteAttributeString("type", m_eType.ToString());

            // flags
            w.WriteStartAttribute("flags");
            w.WriteValue(m_eFlags.ToString());
            w.WriteEndAttribute();

            // local matrix
            {
                w.WriteStartElement("local");
                //w.WriteAttributeString("type", m_oController.GetType().ToString());
                XmlSerializer oSrlz = new XmlSerializer(m_mLocal.GetType());
                oSrlz.Serialize(w, m_mLocal, oNs);
                w.WriteEndElement();
            }

            if (m_oChildren != null)
            {
                w.WriteStartElement("children");
                foreach (Node oNode in m_oChildren)
                {
                    w.WriteStartElement("node");
                    w.WriteAttributeString("type", oNode.GetType().ToString());
                    XmlSerializer oSrlz = new XmlSerializer(oNode.GetType());
                    oSrlz.Serialize(w, oNode, oNs);
                    w.WriteEndElement();
                }
                w.WriteEndElement();
            }

            /*

            // effects
            w.WriteStartElement("effects");
            foreach (StateEffect oEffect in m_oEffects)
            {
                w.WriteStartElement("effect");
                w.WriteAttributeString("type", oEffect.GetType().ToString());
                XmlSerializer oSrlz = new XmlSerializer(oEffect.GetType());
                oSrlz.Serialize(w, oEffect, oNs);
                w.WriteEndElement();
            }
            w.WriteEndElement();*/
        }
        public void ReadXml(XmlReader r)
        {
            // type
            m_eType = (NodeType)Enum.Parse(typeof(NodeType), r.GetAttribute("type"));

            // flags
            m_eFlags = (Flag)Enum.Parse(typeof(Flag), r.GetAttribute("flags"));
            m_eFlags |= Flag.E_LOCAL_CHANGED;

            // local matrix
            {
                XmlSerializer oSrlz = new XmlSerializer(typeof(Mat2x3));
                r.ReadToFollowing("local");
                m_mLocal = (Mat2x3)oSrlz.Deserialize(r);
            }

            // effects
            /*m_oEffects.Clear();
            if (r.ReadToFollowing("effects"))
            {
                XmlReader r2 = r.ReadSubtree();
                while (r2.ReadToFollowing("effect"))
                {
                    Type oType = Type.GetType(r2.GetAttribute("type"));
                    XmlSerializer oSrlz = new XmlSerializer(oType);
                    r2.ReadToDescendant(oType.Name);
                    StateEffect oEffect = (StateEffect)oSrlz.Deserialize(r2);
                    oEffect.Initialize();
                    m_oEffects.Add(oEffect);
                }
            }*/
        }
        #endregion

        #region Attributes
        protected NodeType m_eType;
        public NodeType eType { get { return m_eType; } }
        protected Flag m_eFlags = Flag.E_NONE;
        public Flag eFlags { get { return m_eFlags; } }
        #region Hierarchy
        private Node m_oParent;
        [XmlIgnore]
        public Node oParent
        { 
            get { return m_oParent; } 
        }
        protected Node m_oRoot;
        [XmlIgnore]
        public Node oRoot
        {
            get { return m_oRoot; }
        }
        private List<Node> m_oChildren;
        [XmlIgnore]
        public List<Node> oChildren { get { return m_oChildren; } }
        #endregion
        #region Transform
        protected Mat2x3 m_mLocal = Mat2x3.Identity;
        [XmlIgnore]
        public Mat2x3 mLocal
        {
            get { return m_mLocal; }
            set 
            { 
                m_mLocal = value;
                m_eFlags |= Flag.E_LOCAL_CHANGED;
            }
        }
        protected Mat2x3 m_mWorld = Mat2x3.Identity;
        [XmlIgnore]
        public Mat2x3 mWorld { get { return m_mWorld; } }
        #endregion
        #endregion
    }
}
